package pt.inescid.components.policyengine.heimdall.pip;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Vector;

import pt.inescid.components.policyengine.heimdall.commons.Log;
import pt.inescid.components.policyengine.heimdall.commons.ProjectInfo;
import pt.inescid.components.policyengine.heimdall.commons.Utilities;
import pt.inescid.components.policyengine.heimdall.pdp.base.CesInstance;
import pt.inescid.components.policyengine.heimdall.pdp.base.ModelInstance;

public class NodeInfo implements Cloneable {

	public static final String[] NODE_STATUS_STRINGS = {
		"UNDEFINED",
		"CREATED",
		"WAIT_INFO",
		"NEW_INFO",
		"TIMEOUT",
		"PROCESSED"
	};
	
	public static final int NODE_UNDEFINED = 0;
	public static final int NODE_CREATED = 1;
	public static final int NODE_WAITING_INFO = 2;
	public static final int NODE_NEW_INFO = 3;
	public static final int NODE_TIMEOUT = 4;
	public static final int NODE_PROCESSED = 5;

	public static final int FLAG_USE_CACHE = 1;
	public static final int FLAG_RETRIEVE_ON_STARTUP = 2;
	public static final int FLAG_FORCE_RETRIEVAL = 3;

	
	
	private static String localHostname = null;
	
	private String hostname = null;
	private int hostport = Utilities.UNDEFINED_INT_VALUE;
	private int status = NODE_UNDEFINED;
	private Vector procEvents = null;
	private Vector unprocEvents = null;
	private Object value = null;

	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      Constructor       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	private NodeInfo() {		
	}
	
	
	public NodeInfo(String name, int port) {
		this.hostname = name;
		this.hostport = port;
		status = NODE_UNDEFINED;
		procEvents = new Vector();
		unprocEvents = new Vector();
	}


	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getLocalNode       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public static NodeInfo getLocalNode() {
		NodeInfo localNode = new NodeInfo(
				ProjectInfo.getProperty(ProjectInfo.HOST_NAME_PROPERTY),
				new Integer(
						ProjectInfo.getProperty(
								ProjectInfo.HOST_PORT_PROPERTY)).intValue());
		
		return (localNode);
	} // getLocalNode


	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      addEvents       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public void addEvents(Vector newEvents) {
		Utilities.joinVectors(unprocEvents, newEvents);
	} // addEvents
	
	
	public int getStatus() {
		return status;
	}

	public void setStatus(int status) {
		this.status = status;
	}

	public Object getValue() {
		return value;
	}

	public void setValue(Object value) {
		this.value = value;
	}

	public String toString() {
		String result = hostname + ":" +
					hostport + "[" +  NODE_STATUS_STRINGS[status] + "]";
		
		if (value != null) {
			result += value.toString() + "\n";
		}

		return (result);
	}
	
	public boolean isLocal() {
	    return (hostname.equals(ProjectInfo.getProperty(
	    			ProjectInfo.HOST_NAME_PROPERTY)) &&
	    		hostport == Integer.parseInt(
	    				ProjectInfo.getProperty(
	    						ProjectInfo.HOST_PORT_PROPERTY)));
	}

	//  ----------------------------------------------------------
//  ------------------------   getUnprocessedEvents   ------------------------
//  ----------------------------------------------------------
	public Vector getUnprocessedEvents(ModelInstance currentModel,
				CesInstance cesInstance, 
				String domainName, 
				Vector cesParameters, boolean onlyCached) {

		Log.debug(Log.INFO, "Arrived.", "NodeInfo.getUnprocessedEvents");
		
		if (status == NODE_UNDEFINED ||
				cesInstance.getDefinition().isObligation()) {
			unprocEvents = PolicyPIP.getInstance().
				getEvents(currentModel,
					this, 
					cesInstance,
					domainName, cesParameters, onlyCached);
		}
		
		if (status == NODE_NEW_INFO) {
			Vector result = new Vector();
			Utilities.joinVectors(result, unprocEvents);
			
			Utilities.joinVectors(procEvents, unprocEvents);
			unprocEvents = new Vector();
			
			status = NODE_PROCESSED;
			
			return (result);
		}
		
		// if the node was already processed, it is accounted for in ces.value field
		return (null);
	} // getUnprocessedEvents


	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      getLoadedEvents       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public Vector getLoadedEvents() {

		Log.debug(Log.INFO, "Arrived.", "NodeInfo.getLoadedEvents");
		
		Vector result = new Vector();
		Utilities.joinVectors(result, procEvents);
		Utilities.joinVectors(result, unprocEvents);
		
		return (result);
	} // getLoadedEvents


	
	
	public void setUnprocEvents(Vector unprocEvents) {
		this.unprocEvents = unprocEvents;
	}

	public Vector getProcEvents() {
		return procEvents;
	}

	public void setProcEvents(Vector procEvents) {
		this.procEvents = procEvents;
	}

	public static String getLocalHostname() {
		if (localHostname == null) {
	        InetAddress addr = null;
	 		try {
		        addr = InetAddress.getLocalHost();
		    } catch (UnknownHostException e) {
		    	Log.debug(Log.ERROR, "Error in geting local hostname", 
		    			"NodeInfo.");
		    	return ("");
		    }
		    
			localHostname = addr.getCanonicalHostName();
		}
		
		return (localHostname);
	} // getLocalHostname

	public String getHostname() {
		return hostname;
	}

	public int getHostport() {
		return hostport;
	}

	public void setHostname(String hostname) {
		this.hostname = hostname;
	}

	public void setHostport(int hostport) {
		this.hostport = hostport;
	}

	
	public boolean equals(Object arg0) {
		NodeInfo otherNode = (NodeInfo) arg0;
		
		return (this.hostname.equals(otherNode.getHostname()) &&
				this.hostport == otherNode.getHostport());
	}
	
	
	public Object clone() throws CloneNotSupportedException {
		NodeInfo clonedInstance = new NodeInfo();
		
		clonedInstance.hostname = this.hostname;
		clonedInstance.hostport = this.hostport;
		clonedInstance.status = NODE_UNDEFINED;
		clonedInstance.procEvents = new Vector();
		clonedInstance.unprocEvents = new Vector();
		clonedInstance.value = null;
		
		return (clonedInstance);
	}


}; // NodeInfo
